﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using AssemblerCompiler.IntelliSense;
using AssemblerCompiler.Properties;

namespace AssemblerCompiler
{
    public partial class Form1 : Form
    {
        private CodeHighlight codeHighliter;
        private string path;

        private TranslationResult tr;

        public Form1()
        {
            this.InitializeComponent();

            this.codeHighliter = new CodeHighlight(this.codeTextBox);

            var img = new DataGridViewImageColumn();
            img.Image = (Image) Resources.ResourceManager.GetObject("error");
            img.Width = 20;
            img.MinimumWidth = 20;
            img.Resizable = DataGridViewTriState.False;
            this.errorGridView.Columns.Insert(0, img);
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.path != null)
            {
                DialogResult dr = MessageBox.Show("Save changes in current file?", "", MessageBoxButtons.YesNoCancel);
                if (dr == DialogResult.Yes)
                {
                    this.Save();
                }
                if (dr == DialogResult.Cancel)
                {
                    return;
                }
                this.Close();
            }
            var saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "ASM files (*.asm) |*.asm";
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                this.path = saveFileDialog1.FileName;
                var sw = new StreamWriter(this.path);
                sw.Close();
                this.codeTextBox.Visible = true;
                this.saveToolStripMenuItem.Enabled = true;
                this.codeTextBox.Enabled = true;
                this.codeTextBox.Text = "";
                this.example1ToolStripMenuItem.Enabled = true;
                this.example2ToolStripMenuItem.Enabled = true;
            }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.path != null)
            {
                DialogResult dr = MessageBox.Show("Save changes in current file?", "", MessageBoxButtons.YesNoCancel);
                if (dr == DialogResult.Yes)
                {
                    this.Save();
                }
                if (dr == DialogResult.Cancel)
                {
                    return;
                }
                this.Close();
            }
            var openFileDialog1 = new OpenFileDialog();
            openFileDialog1.Filter = "ASM files (*.asm) |*.asm";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                this.path = openFileDialog1.FileName;
                var sr = new StreamReader(this.path);
                this.codeTextBox.Enabled = true;
                this.codeTextBox.Text = sr.ReadToEnd();
                sr.Close();
                this.codeTextBox.Visible = true;
                this.saveToolStripMenuItem.Enabled = true;
                this.example1ToolStripMenuItem.Enabled = true;
                this.example2ToolStripMenuItem.Enabled = true;
            }
        }

        private void errorGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            int lineNumber = int.Parse(this.errorGridView.Rows[e.RowIndex].Cells[2].Value.ToString()) - 1;
            int selectionStart = this.codeTextBox.GetFirstCharIndexFromLine(lineNumber);
            this.codeTextBox.SelectionLength = 0;
            this.codeTextBox.SelectionStart = selectionStart + 1;
            //this.codeTextBox.Select(selectionStart, 0);
            this.codeTextBox.ScrollToCaret();
        }

        private void Save()
        {
            var sw = new StreamWriter(this.path);
            sw.Write(this.codeTextBox.Text);
            sw.Close();
        }

        private void Close()
        {
            this.path = null;
            this.saveToolStripMenuItem.Enabled = false;
            this.codeTextBox.Visible = false;
            this.codeTextBox.Text = "";
            this.errorGridView.Rows.Clear();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Save();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
        
        private void HighlightErrors()
        {
            for (int i = 0; i < this.errorGridView.Rows.Count; i++)
            {
                int lineNumber = int.Parse(this.errorGridView.Rows[i].Cells[2].Value.ToString()) - 1;
                int selectionStart = this.codeTextBox.GetFirstCharIndexFromLine(lineNumber);
                this.codeTextBox.Select(selectionStart, this.codeTextBox.Lines[lineNumber].Length);
                this.codeTextBox.SelectionColor = Color.Red;
            }
        }

        private void runToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.tr = Compiler.Compile(this.codeTextBox.Text);

            this.errorGridView.Rows.Clear();

            if (this.tr.ErrorReports.Count > 0)
            {
                this.errorGridResizing();
                this.countErrorsLabel.Text = "There are " + this.tr.ErrorReports.Count.ToString() + " error" +
                                             ((this.tr.ErrorReports.Count == 1) ? "" : "s") + "!";
                this.countErrorsLabel.Visible = true;
                this.errorImagePanel.Visible = true;
                this.errorGridView.Visible = true;
                this.codeTextBox.Height = 333;
                this.codeTextBox.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);
                this.errorGridView.Rows.Clear();

                int i = 0;
                foreach (var k in this.tr.ErrorReports)
                {
                    this.errorGridView.Rows.Add();
                    this.errorGridView.Rows[i].Cells[1].Value = k.Value;
                    this.errorGridView.Rows[i].Cells[2].Value = k.Key;
                    this.errorGridView.Rows[i].Cells[3].Value = "build";
                    ++i;
                }

                this.HighlightErrors();
                return;
            }
            else
            {
                this.countErrorsLabel.Text = "There are no errors!";
                this.countErrorsLabel.Visible = false;
                this.errorImagePanel.Visible = false;
                this.errorGridView.Visible = false;
                this.codeTextBox.Height = 505;
                this.codeTextBox.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right |
                                           AnchorStyles.Bottom);
            }
            this.errorGridView.Rows.Add();
            this.errorGridView.Rows[this.errorGridView.Rows.Count - 1].Cells[1].Value = "Compilation successful!";
            this.errorGridView.Rows[this.errorGridView.Rows.Count - 1].Cells[2].Value = "";
            this.errorGridView.Rows[this.errorGridView.Rows.Count - 1].Cells[3].Value = "";

            try
            {
                string path1 = this.path.Substring(0, this.path.Length - 3) + "com";
                var fs = new FileStream(path1, FileMode.Create, FileAccess.Write);
                var bw = new BinaryWriter(fs);
                for (int i = 256; i < this.tr.Code.Length; ++i)
                {
                    bw.Write(this.tr.Code[i]);
                }
                bw.Close();

                //errorGridView.Rows.Add("", "Creating com-file successful!");

                string path2 = this.path.Substring(0, this.path.Length - 3) + "bat";
                var fs1 = new FileStream(path2, FileMode.Create, FileAccess.Write);
                var sw = new StreamWriter(fs1);
                sw.WriteLine("ECHO OFF");
                sw.WriteLine("CLS");
                sw.WriteLine("\"" + path1 + "\"");
                sw.WriteLine("PAUSE");
                sw.WriteLine("EXIT");
                sw.Close();

                //errorGridView.Rows.Add("", "Creating bat-file successful!");

                bool runed = false;
                for (int i = 0; i < 100; ++i)
                {
                    Thread.Sleep(1000);
                    runed = true;
                    try
                    {
                        Process.Start(path2);
                    }
                    catch
                    {
                        runed = false;
                    }
                    if (runed)
                    {
                        break;
                    }
                }
                if (!runed)
                {
                    throw new Exception("Can not run com-file!");
                }

                //errorGridView.Rows.Add("", "Running com-file successful!");
            }
            catch (Exception e_)
            {
                this.errorGridView.Rows.Add();
                this.errorGridView.Rows[this.errorGridView.Rows.Count - 1].Cells[1].Value =
                    "Error while creating executable file! " + e_.Message;
                this.errorGridView.Rows[this.errorGridView.Rows.Count - 1].Cells[2].Value = "";
                this.errorGridView.Rows[this.errorGridView.Rows.Count - 1].Cells[3].Value = "";
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.path != null)
            {
                DialogResult dr = MessageBox.Show("Save changes in current file?", "", MessageBoxButtons.YesNoCancel);
                if (dr == DialogResult.Yes)
                {
                    this.Save();
                }
                if (dr == DialogResult.Cancel)
                {
                    e.Cancel = true;
                    return;
                }
                this.Close();
            }
        }

        private void example1ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.codeTextBox.Enabled)
            {
                this.codeTextBox.Text = "ORG 100H\n" + "PUSH T\n" + "POP T\n" + "MOV T, AX\n" + "MOV AH, 09H\n"
                                        + "LEA DX, STR\n" + "C:\n" + "INT 21H\n" + "LOOP C\n" + "MOV AH, 4CH\n" +
                                        "INT 21H\n" +
                                        "T DW 10\n" + "STR DB 'I$'";
            }
        }

        private void example2ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.codeTextBox.Enabled)
            {
                this.codeTextBox.Text =
                    "ORG 100H\nMOV AH, 09H\nLEA BX, T\nMOV SI, 0\nMOV CX, [BX][SI]+0\nC:\nLEA DX, STR\n" +
                    "MOV TMP, CX\nMOV CX, T1\nC1:\nINT 21H\nLOOP C1\nMOV CX, TMP\nLEA DX, NEWLINE\nINT 21H\nLOOP C\nMOV AH, 4CH\n" +
                    "INT 21H\nSTR DB 'I$'\nNEWLINE DB 10\nDB 13\nDB '$'\nT DW 5\nT1 DW 10\nTMP DW ?";
            }
        }

        private void errorGridResizing()
        {
            int fullWidth = 0;
            foreach (DataGridViewColumn col in this.errorGridView.Columns)
            {
                if (col.Name != "Description")
                {
                    fullWidth += col.Width;
                }
            }

            this.errorGridView.Columns["Description"].Width = this.errorGridView.Width - fullWidth - 8;
        }

        private void errorGridView_Resize(object sender, EventArgs e)
        {
            this.errorGridResizing();
        }

        private void errorGridView_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            this.errorGridResizing();
        }
    }
}